/**
 * ...
 * @author Franco Ponticelli
 */

package uform.model.generator;

import haxe.macro.Context;
import haxe.macro.Expr;
import haxe.macro.Type;
import uform.model.generator.Granularity;

class MacroSchemaGenerator
{
	var definitions : Hash<FieldInfo<Dynamic>>;
	public function new(definitions : Hash<FieldInfo<Dynamic>>)
	{
		this.definitions = definitions;
	}

	public function toString()
	{
		var infos = Lambda.array(definitions);
		infos.sort(function(a, b){
			var wa = null == a.weight ? 0 : a.weight;
			var wb = null == b.weight ? 0 : b.weight;
			return wa - wb;
		});
		
		var buf = new StringBuf();
		buf.add("new uform.model.core.Schema()\n");
		for(info in infos)
		{
			var type : String = null;
			switch(info.fieldType) {
				case ValueField(def):
					assignBounds(info, def);
					var buf = new StringBuf();
					
					buf.add("new " + def.type + "(" + def.args.join(", ") + ")");
					
					for (validator in def.validators)
						buf.add(".addValidator(new " + validator.type + "(" + validator.args.join(", ") + "))");
					
					for (key in def.meta.keys())
						buf.add(".addMeta('" + key + "', " + def.meta.get(key) + ")");
						
					type = buf.toString();
				case DefinitionsField(d):
					var gen = new MacroSchemaGenerator(d);
					type = gen.toString();
				default:
					continue;
			}
			
			var g = null == info.granularity ? Mandatory : info.granularity;
			switch(g)
			{
				case Mandatory:
					var args : Array<Dynamic> = [
						_s(info.name),
						type,
						_s(info.label),
						null == info.expr ? _s(info.defaultValue) : info.expr,
						_s(info.description)
					];
				    buf.add('.add(' + args.join(", ") + ')\n');
				case Optional:
					var args : Array<Dynamic> = [
						_s(info.name),
						type,
						_s(info.label),
						null == info.expr ? _s(info.defaultValue) : info.expr,
						_s(info.description)
					];
				    buf.add('.addOpt(' + args.join(", ") + ')\n');
				case Range(min, max):
					var args : Array<Dynamic> = [
						_s(info.name),
						type,
						min,
						max,
						_s(info.label),
						null == info.expr ? _s(info.defaultValue) : info.expr,
						_s(info.description)
					];
					buf.add('.addMulti(' + args.join(", ") + ')\n');
			}
		}
		return buf.toString();
	}
	
	function assignBounds(info : FieldInfo<Dynamic>, def)
	{
		if (null != info.min || null != info.min)
		{
			switch(def.type)
			{
				case "uform.model.value.DateType":
						def.validators.push( { type : "thx.validation.DateGranularityRangeValidator", args : [
							def.args[0],
							_d(info.min, "null"),
							_d(info.max, "null")
						] });
				case "uform.model.value.FloatType":
					def.validators.push( { type : "thx.validation.RangeValidator", args : [
						_d(info.min, "null"),
						_d(info.max, "null")
					] });
				case "uform.model.value.StringType":
					def.validators.push( { type : "thx.validation.StringLengthValidator", args : [
						_d(info.min, "null"),
						_d(info.max, "null")
					] });
			}
		}
	}
	
	static function _b(v : Dynamic, alt : String) : String
	{
		if(null == v || !Std.is(v, Bool))
			return alt;
		else
			return v == true ? "true" : "false";
	}
	
	static function _i(v : Dynamic, alt : String) : String
	{
		if(null == v || !Std.is(v, Float))
			return alt;
		else
			return "" + Std.int(v);
	}
	
	static function _f(v : Dynamic, alt : String) : String
	{
		if(null == v || !Std.is(v, Float))
			return alt;
		else
			return v;
	}
	
	static function _d(v : Dynamic, alt : String) : String
	{
		if(null == v)
		 	return alt;
	   	else if(Std.is(v, String))
			return DateTools.format(Date.fromString(v), "%Y-%m-%d");
		else if(Std.is(v, Date))
			return DateTools.format(v, "%Y-%m-%d");
		else
			return alt;
	}

	static function _s(v : Dynamic)
	{
		if (null == v)
			return "null";
		else if (Std.is(v, String))
			return '"' + StringTools.replace(v, '"', '\"') + '"';
		else if (Std.is(v, Float))
			return "" + v;
		else if (Std.is(v, Bool))
			return v ? "true" : "false";
		else
			return _s(Std.string(v));
	}
}